package com.dyj.common.service.impl;

import com.dyj.common.domain.ClientTokenInfo;
import com.dyj.common.domain.UserTokenInfo;
import com.dyj.common.domain.vo.AppsV2TokenVo;
import com.dyj.common.domain.vo.BizTokenVo;
import com.dyj.common.domain.vo.TpThirdV2TokenVo;
import com.dyj.common.domain.vo.TpV2AuthTokenVo;
import com.dyj.common.exception.AuthTokenNotFoundException;
import com.dyj.common.service.IAgentTokenService;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiFunction;
import java.util.function.Supplier;

/**
 * @author danmo
 * @date 2024-04-07 13:49
 **/
public class CacheAgentTokenServiceImpl implements IAgentTokenService {

    private final Map<String, UserTokenInfo> userTokenMap = new ConcurrentHashMap<>();
    private final Map<String, ClientTokenInfo> clientTokenMap = new ConcurrentHashMap<>();
    private final Map<String,AppsV2TokenVo> appsV2TokenMap = new ConcurrentHashMap<>();
    private final Map<String,Lock> lockMap = new ConcurrentHashMap<>();

    @Override
    public void setUserTokenInfo(Integer tenantId, String clientKey, String accessToken, Long expiresIn, String refreshToken, Long refreshExpiresIn, String openId) throws AuthTokenNotFoundException {
        UserTokenInfo userTokenInfo = new UserTokenInfo();
        userTokenInfo.setAccessToken(accessToken);
        userTokenInfo.setExpiresIn(expiresIn);
        userTokenInfo.setRefreshToken(refreshToken);
        userTokenInfo.setRefreshExpiresIn(refreshExpiresIn);
        userTokenInfo.setOpenId(openId);
        userTokenMap.put(String.format("%s_%s_%s", tenantId, clientKey, openId), userTokenInfo);
    }

    @Override
    public void updateRefreshTokenInfo(Integer tenantId, String clientKey, String refreshToken, Long refreshExpiresIn, String openId) throws AuthTokenNotFoundException {
        UserTokenInfo userTokenInfo = userTokenMap.get(String.format("%s_%s_%s", tenantId, clientKey, openId));
        if (Objects.isNull(userTokenInfo)) {
            throw new AuthTokenNotFoundException("token not found");
        }
        userTokenInfo.setRefreshToken(refreshToken);
        userTokenInfo.setRefreshExpiresIn(refreshExpiresIn);
        userTokenMap.put(String.format("%s_%s_%s", tenantId, clientKey, openId), userTokenInfo);
    }

    @Override
    public UserTokenInfo getUserTokenInfo(Integer tenantId, String clientKey, String openId) throws AuthTokenNotFoundException {
        UserTokenInfo userTokenInfo = userTokenMap.get(String.format("%s_%s_%s", tenantId, clientKey, openId));
        if (Objects.isNull(userTokenInfo)) {
            throw new AuthTokenNotFoundException("token not found");
        }
        return userTokenInfo;
    }


    @Override
    public String getRefreshToken(Integer tenantId, String clientKey, String openId) throws AuthTokenNotFoundException {
        UserTokenInfo userTokenInfo = userTokenMap.get(String.format("%s_%s_%s", tenantId, clientKey, openId));
        if (Objects.isNull(userTokenInfo)) {
            throw new AuthTokenNotFoundException("token not found");
        }
        return userTokenInfo.getRefreshToken();
    }

    @Override
    public ClientTokenInfo setClientTokenInfo(Integer tenantId, String clientKey, String accessToken, Long expiresIn) {
        ClientTokenInfo clientTokenInfo = new ClientTokenInfo();
        clientTokenInfo.setAccessToken(accessToken);
        clientTokenInfo.setExpiresIn(expiresIn);
        clientTokenMap.put(String.format("%s_%s", tenantId, clientKey), clientTokenInfo);
        return clientTokenInfo;
    }

    @Override
    public ClientTokenInfo getClientTokenInfo(Integer tenantId, String clientKey) throws AuthTokenNotFoundException {
        return clientTokenMap.get(String.format("%s_%s", tenantId, clientKey));
    }

    @Override
    public ClientTokenInfo computeClientTokenInfoIfAbsent(Integer tenantId, String clientKey, Supplier<ClientTokenInfo> mappingSupplier) throws AuthTokenNotFoundException {
        String key = String.format("%s_%s", tenantId, clientKey);
        ClientTokenInfo clientTokenInfo = clientTokenMap.get(key);
        if (clientTokenInfo != null) {
            return clientTokenInfo;
        }
        Lock lock = lockMap.computeIfAbsent(key +"-clientToken", k -> new ReentrantLock());
        lock.lock();
        try {
            clientTokenInfo = clientTokenMap.get(key);
            if (clientTokenInfo != null) {
                return clientTokenInfo;
            }
            clientTokenInfo = mappingSupplier.get();
            if (clientTokenInfo == null) {
                throw new AuthTokenNotFoundException("clientToken获取失败");
            }
            clientTokenMap.put(key, clientTokenInfo);
            return clientTokenInfo;
        }finally {
            lock.unlock();
        }
    }

    @Override
    public BizTokenVo getBizToken(Integer tenantId, String clientKey, String openId) {
        return null;
    }

    @Override
    public void setBizToken(Integer tenantId, String clientKey, String bizToken, Long bizExpiresIn, String bizRefreshToken, Long bizRefreshExpiresIn, String openId) {

    }

    @Override
    public void setAppsV2Token(Integer tenantId, String clientKey, String accessToken, Long expiresIn) {

    }

    @Override
    public AppsV2TokenVo getAppsV2Token(Integer tenantId, String clientKey) {
        return null;
    }

    @Override
    public AppsV2TokenVo computeAppsV2TokenIfAbsent(Integer tenantId, String clientKey, Supplier<AppsV2TokenVo> mappingSupplier) throws AuthTokenNotFoundException {
        String key = String.format("%s_%s", tenantId, clientKey);
        AppsV2TokenVo appsV2TokenVo = appsV2TokenMap.get(key);
        if (appsV2TokenVo != null) {
            return appsV2TokenVo;
        }
        Lock lock = lockMap.computeIfAbsent(key +"-appsV2Token", k -> new ReentrantLock());
        lock.lock();
        try {
            appsV2TokenVo = appsV2TokenMap.get(key);
            if (appsV2TokenVo != null) {
                return appsV2TokenVo;
            }
            appsV2TokenVo = mappingSupplier.get();
            if (appsV2TokenVo == null) {
                throw new AuthTokenNotFoundException("appsV2Token获取失败");
            }
            appsV2TokenMap.put(key, appsV2TokenVo);
            return appsV2TokenVo;
        }finally {
            lock.unlock();
        }
    }

    @Override
    public TpThirdV2TokenVo getTpThirdV2Token(Integer tenantId, String clientKey) {
        return null;
    }

    @Override
    public void setTpThirdV2Token(Integer tenantId, String clientKey, TpThirdV2TokenVo tpThirdV2Token) {

    }

    @Override
    public TpV2AuthTokenVo getTpV2AuthToken(Integer tenantId, String clientKey) {
        return null;
    }

    @Override
    public void setTpV2AuthToken(Integer tenantId, String clientKey, TpV2AuthTokenVo tpV2AuthToken) {

    }


    @Override
    public TpThirdV2TokenVo computeTpThirdV2TokenIfAbsent(Integer tenantId, String clientKey, Supplier<TpThirdV2TokenVo> mappingSupplier) throws AuthTokenNotFoundException {
        return null;
    }

    @Override
    public TpV2AuthTokenVo computeTpV2AuthTokenIfAbsent(Integer tenantId, String clientKey, Supplier<TpV2AuthTokenVo> mappingSupplier) throws AuthTokenNotFoundException {
        return null;
    }
}
